/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_zml.htm
 *
 * Zhiqim Zml is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.zml;

import java.util.ArrayList;
import java.util.List;

import org.zhiqim.kernel.extend.HashMapSO;
import org.zhiqim.kernel.extend.MapS;
import org.zhiqim.kernel.extend.HashMapSV;
import org.zhiqim.kernel.extend.MapSO;
import org.zhiqim.kernel.extend.MapSV;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.zml.exception.ExpressionException;
import org.zhiqim.zml.exception.StatementException;
import org.zhiqim.zml.statement._Function;
import org.zhiqim.zml.statement._Var;

/**
 * ZhiqimML变量表<br><br>
 * 
 * 1、最高优先级__局部变量表，按排序先进优先级更高<br>
 * 2、次优化级__页面全局变量表，来自Action属性表和URL参数表，重名时属性覆盖参数<br>
 * 3、三级优化级__请求级变量表，来自请求、响应、会话和上下文环境变量定义表<br>
 * 4、四级优化级__上下文定义变量表，由工程配置启动时指定对象<br>
 * 5、最低优化级__框架中定义的工程全局变量表，由框架中负责更新<br>
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class ZmlVariable
{
    private Zml zml;
    
    private MapSO variableMap;
    private MapS[] contextMap;
    private MapSV<_Function> functionMap;
    
    //局部变量，用于_If,_For,_Function
    private List<MapSO> localList;
    
    /**************************************************************************************************/
    //设置变量&函数
    /**************************************************************************************************/
    
    public void setZml(Zml zml)
    {
        this.zml = zml;
    }
    
    public void setVariableMap(MapSO variableMap)
    {
        this.variableMap = variableMap;
    }
    
    public void setContextMap(MapS... contextMap)
    {
        this.contextMap = contextMap;
    }
    
    public void setFunctionMap(MapSV<_Function> functionMap)
    {
        this.functionMap = functionMap;
    }
    
    public void addFunction(_Function function) throws StatementException
    {
        if (functionMap == null)
            functionMap = new HashMapSV<>();
        else if (functionMap.containsKey(function.getName()))
            throw new StatementException(function, "存在相同的函数名["+function.getName()+"]");
        
        functionMap.put(function.getName(), function);
    }
    
    public void addLocalVariable(MapSO localMap)
    {
        if (localList == null)
            localList = new ArrayList<>(2);
        
        localList.add(localMap);
    }
    
    public void removeLocalVariable(MapSO localMap)
    {
        localList.remove(localMap);
    }
    
    /**
     * 增加变量值，
     * 1）如果没有局部变量表，则增加到页变量表
     * 2）有局部变量表，且存在变量定义，更新到局部变量
     * 3）有局部变量表，但不在局变量表中，且在页变量表中，认为是更新页变量表，更新到页变量表
     * 4）有局部变量表，但不在局变量表和页变量表中，认为是新增局变量，在最内层增加。
     * 
     * @param name      变量名
     * @param value     变量值
     */
    public void addVariable(String name, Object value)
    {
        //1.如果没有局部作用域，则直接放置到全页面作用域
        if (localList == null || localList.isEmpty())
        {
            if (variableMap == null)
                variableMap = new HashMapSO();
            
            variableMap.put(name, value);
            return;
        }
        
        //2.先检查局部作用域，如果存在定义则赋值到局部作用域
        for (int i=localList.size()-1;i>=0;i--)
        {
            MapSO localMap = localList.get(i);
            if (localMap.containsKey(name))
            {
                localMap.put(name, value);
                return;
            }
        }
        
        //3.再检查全页面作用域，如果在全页面作用域有定义，则赋值到全页面作用域
        if (variableMap != null && variableMap.containsKey(name))
        {
            variableMap.put(name, value);
            return;
        }
        
        //4.如果局部和全页面都没找到定义，则在最内层局部作用域增加变量
        MapSO localMap = localList.get(localList.size()-1);
        localMap.put(name, value);
    }
    
    /**************************************************************************************************/
    //获取基本信息
    /**************************************************************************************************/
    
    public Zml getZml()
    {
        return zml;
    }
    
    public ZmlEngine getEngine()
    {
        return zml.getEngine();
    }
    
    public MapSO getVariableMap()
    {
        return variableMap;
    }
    
    public MapS[] getContextMap()
    {
        return contextMap;
    }
    
    public MapSV<_Function> getFunctionMap()
    {
        return functionMap;
    }
    
    /**************************************************************************************************/
    //获取变量&函数
    /**************************************************************************************************/
    
    public Object get(String key)
    {
        //1.在if,for局部作用域中找
        if (localList != null && !localList.isEmpty())
        {
            for (int i=localList.size()-1;i>=0;i--)
            {
                MapSO loaclMap = localList.get(i);
                Object value = loaclMap.get(key);
                if (value != null)
                    return chkDynamicVar(value);
            }
        }
                
        //2.在全页面作用域中找
        if (variableMap != null)
        {
            Object value = variableMap.get(key);
            if (value != null)
                return chkDynamicVar(value);
        }
        
        //3.在context作用域中找
        if (contextMap != null)
        {
            for (MapS map : contextMap)
            {
                Object value = map.get(key);
                if (value != null)
                    return chkDynamicVar(value);
            }
        }
        
        //4.在引擎作用域中找
        ZmlEngine engine = zml.getEngine();
        if (engine != null)
        {
            //5.1 在引擎global作用域中找
            Object value = engine.getGlobalVariable(key);
            if (value != null)
                return chkDynamicVar(value);
            
            //5.2 在引擎config变量中找
            _Var var = engine.getVar(key);
            if (var != null)
            {
                try
                {
                    return var.getExpression().build(this);
                }
                catch (ExpressionException e)
                {
                    throw Asserts.exception("执行全局变量表达式时异常["+var.getVariableName()+"]", e);
                }
            }
        }
        
        //5.在system作用域中找
        return ZmlEngine.getSystemVariable(key);
    }
    
    public _Function getFunction(String name) throws StatementException
    {
        _Function function = (functionMap == null)?null:functionMap.get(name);
        if (function != null)
            return function;
        else
            return zml.getFunction(name);
    }
    
    /**************************************************************************************************/
    //对动态变量进行处理
    /**************************************************************************************************/
    
    public static Object chkDynamicVar(Object value)
    {
        return (value instanceof ZmlVarRuntime)?((ZmlVarRuntime)value).build():value;
    }
}
